msg_tool\scripts\softpal\scr/
disasm.rs

1use crate::ext::io::*;
2use anyhow::Result;
3use int_enum::IntEnum;
4use std::collections::HashMap;
5use std::io::{Read, Write};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8enum Oper {
9    P,
10    I,
11    L,
12}
13
14use Oper::*;
15
16const OPS: [(u16, (Option<&'static str>, &'static [Oper])); 210] = [
17    (0x0001, (Some("mov"), &[P, P])),
18    (0x0002, (Some("add"), &[P, P])),
19    (0x0003, (Some("sub"), &[P, P])),
20    (0x0004, (Some("mul"), &[P, P])),
21    (0x0005, (Some("div"), &[P, P])),
22    (0x0006, (Some("binand"), &[P, P])),
23    (0x0007, (Some("binor"), &[P, P])),
24    (0x0008, (Some("binxor"), &[P, P])),
25    (0x0009, (Some("jmp"), &[L])),
26    (0x000A, (Some("jz"), &[L, P])),
27    (0x000B, (Some("call"), &[L])),
28    (0x000C, (Some("eq"), &[P, P])),
29    (0x000D, (Some("neq"), &[P, P])),
30    (0x000E, (Some("le"), &[P, P])),
31    (0x000F, (Some("ge"), &[P, P])),
32    (0x0010, (Some("lt"), &[P, P])),
33    (0x0011, (Some("gt"), &[P, P])),
34    (0x0012, (Some("logor"), &[P, P])),
35    (0x0013, (Some("logand"), &[P, P])),
36    (0x0014, (Some("not"), &[I])),
37    (0x0015, (Some("exit"), &[])),
38    (0x0016, (Some("nop"), &[])),
39    (0x0017, (Some("syscall"), &[I, I])),
40    (0x0018, (Some("ret"), &[])),
41    (0x0019, (None, &[])),
42    (0x001A, (Some("mod"), &[P, P])),
43    (0x001B, (Some("shl"), &[P, P])),
44    (0x001C, (Some("sar"), &[P, P])),
45    (0x001D, (Some("neg"), &[I])),
46    (0x001E, (Some("pop"), &[P])),
47    (0x001F, (Some("push"), &[P])),
48    (0x0020, (Some("enter"), &[P])),
49    (0x0021, (Some("leave"), &[P])),
50    (0x0023, (Some("create_message"), &[])),
51    (0x0024, (Some("get_message"), &[])),
52    (0x0025, (Some("get_message_param"), &[])),
53    (0x0028, (Some("se_load"), &[])),
54    (0x0029, (Some("se_play"), &[])),
55    (0x002A, (Some("se_play_ex"), &[])),
56    (0x002B, (Some("se_stop"), &[])),
57    (0x002C, (Some("se_set_volume"), &[])),
58    (0x002D, (Some("se_get_volume"), &[])),
59    (0x002E, (Some("se_unload"), &[])),
60    (0x002F, (Some("se_wait"), &[])),
61    (0x0030, (Some("set_se_info"), &[])),
62    (0x0031, (Some("get_se_ex_volume"), &[])),
63    (0x0032, (Some("set_se_ex_volume"), &[])),
64    (0x0033, (Some("se_enable"), &[])),
65    (0x0034, (Some("is_se_enable"), &[])),
66    (0x0035, (Some("se_set_pan"), &[])),
67    (0x0036, (Some("se_mute"), &[])),
68    (0x0038, (Some("select_init"), &[])),
69    (0x0039, (Some("select"), &[])),
70    (0x003A, (Some("select_add_choice"), &[])),
71    (0x003B, (Some("end_select"), &[])),
72    (0x003C, (Some("select_clear"), &[])),
73    (0x003D, (Some("select_set_offset"), &[])),
74    (0x003E, (Some("select_set_process"), &[])),
75    (0x003F, (Some("select_lock"), &[])),
76    (0x0040, (Some("get_select_on_key"), &[])),
77    (0x0041, (Some("get_select_pull_key"), &[])),
78    (0x0042, (Some("get_select_push_key"), &[])),
79    (0x0044, (Some("skip_set"), &[])),
80    (0x0045, (Some("skip_is"), &[])),
81    (0x0046, (Some("auto_set"), &[])),
82    (0x0047, (Some("auto_is"), &[])),
83    (0x0048, (Some("auto_set_time"), &[])),
84    (0x0049, (Some("auto_get_time"), &[])),
85    (0x004A, (Some("window_set_mode"), &[])),
86    (0x004B, (None, &[])),
87    (0x004C, (None, &[])),
88    (0x004D, (None, &[])),
89    (0x004E, (None, &[])),
90    (0x004F, (Some("effect_enable_is"), &[])),
91    (0x0050, (Some("cursor_pos_get"), &[])),
92    (0x0051, (Some("time_get"), &[])),
93    (0x0052, (None, &[])),
94    (0x0053, (Some("load_font"), &[])),
95    (0x0054, (Some("unload_font"), &[])),
96    (0x0055, (Some("set_font_type"), &[])),
97    (0x0056, (Some("key_cancel"), &[])),
98    (0x0057, (Some("set_font_color"), &[])),
99    (0x0058, (Some("load_font_ex"), &[])),
100    (0x0059, (None, &[])),
101    (0x005A, (None, &[])),
102    (0x005B, (Some("lpush"), &[])),
103    (0x005C, (Some("lpop"), &[])),
104    (0x005D, (None, &[])),
105    (0x005E, (None, &[])),
106    (0x005F, (Some("set_font_size"), &[])),
107    (0x0060, (Some("get_font_size"), &[])),
108    (0x0061, (Some("get_font_type"), &[])),
109    (0x0062, (Some("set_font_effect"), &[])),
110    (0x0063, (Some("get_font_effect"), &[])),
111    (0x0064, (Some("get_pull_key"), &[])),
112    (0x0065, (Some("get_on_key"), &[])),
113    (0x0066, (Some("get_push_key"), &[])),
114    (0x0067, (Some("input_clear"), &[])),
115    (0x0068, (Some("change_window_size"), &[])),
116    (0x0069, (Some("change_aspect_mode"), &[])),
117    (0x006A, (Some("aspect_position_enable"), &[])),
118    (0x006B, (None, &[])),
119    (0x006C, (Some("get_aspect_mode"), &[])),
120    (0x006D, (Some("get_monitor_size"), &[])),
121    (0x006E, (Some("get_window_pos"), &[])),
122    (0x006F, (Some("get_system_metrics"), &[])),
123    (0x0070, (Some("set_system_path"), &[])),
124    (0x0071, (Some("set_allmosaicthumbnail"), &[])),
125    (0x0072, (Some("enable_window_change"), &[])),
126    (0x0073, (Some("is_enable_window_change"), &[])),
127    (0x0074, (Some("set_cursor"), &[])),
128    (0x0075, (Some("set_hide_cursor_time"), &[])),
129    (0x0076, (Some("get_hide_cursor_time"), &[])),
130    (0x0077, (Some("scene_skip"), &[])),
131    (0x0078, (Some("cancel_scene_skip"), &[])),
132    (0x0079, (Some("lsize"), &[])),
133    (0x007A, (Some("get_async_key"), &[])),
134    (0x007B, (Some("get_font_color"), &[])),
135    (0x007C, (Some("get_current_date"), &[])),
136    (0x007D, (Some("history_skip"), &[])),
137    (0x007E, (Some("cancel_history_skip"), &[])),
138    (0x007F, (None, &[])),
139    (0x0081, (Some("system_btn_set"), &[])),
140    (0x0082, (Some("system_btn_release"), &[])),
141    (0x0083, (Some("system_btn_enable"), &[])),
142    (0x0086, (Some("text_init"), &[])),
143    (0x0087, (Some("text_set_icon"), &[])),
144    (0x0088, (Some("text"), &[])),
145    (0x0089, (Some("text_hide"), &[])),
146    (0x008A, (Some("text_show"), &[])),
147    (0x008B, (Some("text_set_btn"), &[])),
148    (0x008C, (Some("text_uninit"), &[])),
149    (0x008D, (Some("text_set_rect"), &[])),
150    (0x008E, (Some("text_clear"), &[])),
151    (0x008F, (None, &[])),
152    (0x0090, (Some("text_get_time"), &[])),
153    (0x0091, (Some("text_window_set_alpha"), &[])),
154    (0x0092, (Some("text_voice_play"), &[])),
155    (0x0093, (None, &[])),
156    (0x0094, (Some("text_set_icon_animation_time"), &[])),
157    (0x0095, (Some("text_w"), &[])),
158    (0x0096, (Some("text_a"), &[])),
159    (0x0097, (Some("text_wa"), &[])),
160    (0x0098, (Some("text_n"), &[])),
161    (0x0099, (Some("text_cat"), &[])),
162    (0x009A, (Some("set_history"), &[])),
163    (0x009B, (Some("is_text_visible"), &[])),
164    (0x009C, (Some("text_set_base"), &[])),
165    (0x009D, (Some("enable_voice_cut"), &[])),
166    (0x009E, (Some("is_voice_cut"), &[])),
167    (0x009F, (None, &[])),
168    (0x00A0, (None, &[])),
169    (0x00A1, (None, &[])),
170    (0x00A2, (Some("text_set_color"), &[])),
171    (0x00A3, (Some("text_redraw"), &[])),
172    (0x00A4, (Some("set_text_mode"), &[])),
173    (0x00A5, (Some("text_init_visualnovelmode"), &[])),
174    (0x00A6, (Some("text_set_icon_mode"), &[])),
175    (0x00A7, (Some("text_vn_br"), &[])),
176    (0x00A8, (None, &[])),
177    (0x00A9, (None, &[])),
178    (0x00AA, (None, &[])),
179    (0x00AB, (None, &[])),
180    (0x00AC, (Some("tips_get_str"), &[])),
181    (0x00AD, (Some("tips_get_param"), &[])),
182    (0x00AE, (Some("tips_reset"), &[])),
183    (0x00AF, (Some("tips_search"), &[])),
184    (0x00B0, (Some("tips_set_color"), &[])),
185    (0x00B1, (Some("tips_stop"), &[])),
186    (0x00B2, (Some("tips_get_flag"), &[])),
187    (0x00B3, (Some("tips_init"), &[])),
188    (0x00B4, (Some("tips_pause"), &[])),
189    (0x00B6, (Some("voice_play"), &[])),
190    (0x00B7, (Some("voice_stop"), &[])),
191    (0x00B8, (Some("voice_set_volume"), &[])),
192    (0x00B9, (Some("voice_get_volume"), &[])),
193    (0x00BA, (Some("set_voice_info"), &[])),
194    (0x00BB, (Some("voice_enable"), &[])),
195    (0x00BC, (Some("is_voice_enable"), &[])),
196    (0x00BD, (None, &[])),
197    (0x00BE, (Some("bgv_play"), &[])),
198    (0x00BF, (Some("bgv_stop"), &[])),
199    (0x00C0, (Some("bgv_enable"), &[])),
200    (0x00C1, (Some("get_voice_ex_volume"), &[])),
201    (0x00C2, (Some("set_voice_ex_volume"), &[])),
202    (0x00C3, (Some("voice_check_enable"), &[])),
203    (0x00C4, (Some("voice_autopan_initialize"), &[])),
204    (0x00C5, (Some("voice_autopan_enable"), &[])),
205    (0x00C6, (Some("set_voice_autopan"), &[])),
206    (0x00C7, (Some("is_voice_autopan_enable"), &[])),
207    (0x00C8, (Some("voice_wait"), &[])),
208    (0x00C9, (Some("bgv_pause"), &[])),
209    (0x00CA, (Some("bgv_mute"), &[])),
210    (0x00CB, (Some("set_bgv_volume"), &[])),
211    (0x00CC, (Some("get_bgv_volume"), &[])),
212    (0x00CD, (Some("set_bgv_auto_volume"), &[])),
213    (0x00CE, (Some("voice_mute"), &[])),
214    (0x00CF, (Some("voice_call"), &[])),
215    (0x00D0, (Some("voice_call_clear"), &[])),
216    (0x00D2, (Some("wait"), &[])),
217    (0x00D3, (Some("wait_click"), &[])),
218    (0x00D4, (Some("wait_sync_begin"), &[])),
219    (0x00D5, (Some("wait_sync"), &[])),
220    (0x00D6, (Some("wait_sync_end"), &[])),
221    (0x00D7, (None, &[])),
222    (0x00D8, (Some("wait_clear"), &[])),
223    (0x00D9, (Some("wait_click_no_anim"), &[])),
224    (0x00DA, (Some("wait_sync_get_time"), &[])),
225    (0x00DB, (Some("wait_time_push"), &[])),
226    (0x00DC, (Some("wait_time_pop"), &[])),
227];
228const MOV: u16 = 0x0001;
229const CALL: u16 = 0x000B;
230const SYSCALL: u16 = 0x0017;
231const RET: u16 = 0x0018;
232const PUSH: u16 = 0x001F;
233const ENTER: u16 = 0x0020;
234const SELECT_ADD_CHOICE: u16 = 0x003A;
235const TEXT: u16 = 0x0088;
236const TEXT_W: u16 = 0x0095;
237const TEXT_A: u16 = 0x0096;
238const TEXT_WA: u16 = 0x0097;
239const TEXT_N: u16 = 0x0098;
240const TEXT_CAT: u16 = 0x0099;
241pub const CODE_OFFSET: u32 = 0xC;
242const BIN_XOR: u16 = 0x0008;
243
244#[derive(Clone, Copy)]
245struct Operand {
246    offset: u32,
247    raw_value: u32,
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq, IntEnum)]
251#[repr(u32)]
252enum OperandType {
253    Literal = 0,
254    Variable = 4,
255    Argument = 8,
256    UNK = 0xFF,
257}
258
259impl Operand {
260    pub fn typ(&self) -> OperandType {
261        let typ = (self.raw_value >> 28) & 0xF;
262        OperandType::try_from(typ).unwrap_or(OperandType::UNK)
263    }
264
265    pub fn raw_type(&self) -> u32 {
266        (self.raw_value >> 28) & 0xF
267    }
268
269    pub fn value(&self) -> u32 {
270        self.raw_value & 0x0FFFFFFF
271    }
272}
273
274impl std::fmt::Display for Operand {
275    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276        write!(f, "0x{:08X}", self.raw_value)
277    }
278}
279
280struct Instruction {
281    offset: u32,
282    opcode: u16,
283    operands: Vec<Operand>,
284}
285
286impl Instruction {
287    pub fn is_message(&self) -> bool {
288        match self.opcode {
289            TEXT | TEXT_W | TEXT_A | TEXT_WA | TEXT_N | TEXT_CAT => true,
290            SYSCALL => {
291                if self.operands.is_empty() {
292                    false
293                } else {
294                    let raw_value = self.operands[0].raw_value;
295                    match raw_value {
296                        0x20002 | 0x2000F | 0x20010 | 0x20011 | 0x20012 | 0x20013 => true,
297                        _ => false,
298                    }
299                }
300            }
301            _ => false,
302        }
303    }
304}
305
306struct UserMessageFunction {
307    num_args: u32,
308    name_arg_index: u32,
309    message_arg_index: u32,
310}
311
312pub struct Disasm<'a> {
313    reader: MemReaderRef<'a>,
314    label_offsets: Vec<u32>,
315    user_message_functions: HashMap<u32, UserMessageFunction>,
316    variables: HashMap<u32, Operand>,
317    stack: Vec<Operand>,
318    strs: Vec<PalString>,
319    pre_is_hover_text_move: bool,
320    pre_is_hover_text_push: bool,
321    pre_is_hover_text_binxor: bool,
322    hover_text: Option<u32>,
323}
324
325#[derive(Debug)]
326pub enum StringType {
327    Name,
328    Message,
329    /// Hover text
330    Hover,
331    /// Label
332    Label,
333}
334
335impl StringType {
336    pub fn is_label(&self) -> bool {
337        matches!(self, StringType::Label)
338    }
339}
340
341#[derive(Debug)]
342pub struct PalString {
343    pub offset: u32,
344    pub typ: StringType,
345}
346
347impl<'a> Disasm<'a> {
348    pub fn new(data: &'a [u8], label_offsets: &[u32]) -> Result<Self> {
349        let mut reader = MemReaderRef::new(data);
350        let mut magic = [0; 4];
351        reader.read_exact(&mut magic)?;
352        if magic != *b"Sv20" {
353            return Err(anyhow::anyhow!(
354                "Invalid magic number for Softpal script: {:?}",
355                magic
356            ));
357        }
358        Ok(Self {
359            reader,
360            label_offsets: label_offsets.to_vec(),
361            user_message_functions: HashMap::new(),
362            variables: HashMap::new(),
363            stack: Vec::new(),
364            strs: Vec::new(),
365            pre_is_hover_text_move: false,
366            hover_text: None,
367            pre_is_hover_text_push: false,
368            pre_is_hover_text_binxor: false,
369        })
370    }
371
372    pub fn disassemble<W: Write + ?Sized>(
373        mut self,
374        mut writer: Option<&mut W>,
375    ) -> Result<Vec<PalString>> {
376        self.find_user_message_functions()?;
377        self.reader.pos = CODE_OFFSET as usize;
378        let len = self.reader.data.len();
379        while self.reader.pos < len {
380            let instr = self.read_instruction()?;
381            if let Some(writer) = writer.as_mut() {
382                self.write_instruction_to(&instr, writer)?;
383            }
384            let is_hover_text_move = instr.opcode == MOV
385                && instr.operands[0].typ() == OperandType::Variable
386                && instr.operands[0].value() >= 1
387                && instr.operands[1].typ() == OperandType::Literal
388                && instr.operands[1].value() < 0xFFFFFFF;
389            let hover_text = if is_hover_text_move {
390                Some(instr.operands[0].value())
391            } else {
392                None
393            };
394            let mut is_hover_text_push = false;
395            let mut is_hover_text_binxor = false;
396            if instr.is_message() {
397                self.handle_message_instruction()?;
398            } else if instr.opcode == MOV {
399                self.handle_mov_instruction(instr)?;
400            } else if instr.opcode == PUSH {
401                is_hover_text_push = self.handle_push_instruction(instr)?;
402            } else if instr.opcode == CALL {
403                self.handle_call_instruction(instr)?;
404            } else if instr.opcode == SYSCALL {
405                self.handle_syscall_instruction(instr)?;
406            } else if instr.opcode == SELECT_ADD_CHOICE {
407                self.handle_select_choice_instruction()?;
408            } else if instr.opcode == BIN_XOR {
409                if let Some(var) = self.hover_text {
410                    if instr.operands[0].typ() == OperandType::Variable
411                        && instr.operands[0].value() == var - 1
412                        && instr.operands[1].typ() == OperandType::Variable
413                        && instr.operands[1].value() == var - 1
414                    {
415                        is_hover_text_binxor = true;
416                    } else {
417                        self.hover_text = None;
418                    }
419                } else {
420                    self.stack.clear();
421                    self.variables.clear();
422                }
423            } else {
424                self.stack.clear();
425                self.variables.clear();
426            }
427            self.pre_is_hover_text_move = is_hover_text_move;
428            self.pre_is_hover_text_push = is_hover_text_push;
429            self.pre_is_hover_text_binxor = is_hover_text_binxor;
430            if is_hover_text_move {
431                self.hover_text = hover_text;
432            }
433        }
434        Ok(self.strs)
435    }
436
437    fn read_instruction(&mut self) -> Result<Instruction> {
438        let offset = self.reader.pos as u32;
439        let opcode = self.reader.read_u32()?;
440        if (opcode >> 16) != 1 {
441            return Err(anyhow::anyhow!(
442                "Invalid opcode format: 0x{:08X} at offset 0x{:08X}",
443                opcode,
444                offset
445            ));
446        }
447        let opcode = (opcode & 0xFFFF) as u16;
448        let (_, (_, opers)) = OPS.iter().find(|(op, _)| *op == opcode).ok_or_else(|| {
449            anyhow::anyhow!(
450                "Unknown opcode: 0x{:04X} at offset 0x{:08X}",
451                opcode,
452                offset
453            )
454        })?;
455        let mut operands = Vec::new();
456        for _ in *opers {
457            let offset = self.reader.pos as u32;
458            let raw_value = self.reader.read_u32()?;
459            operands.push(Operand { offset, raw_value });
460        }
461        Ok(Instruction {
462            offset,
463            opcode,
464            operands,
465        })
466    }
467
468    fn write_instruction_to(&self, instr: &Instruction, writer: &mut dyn Write) -> Result<()> {
469        let (_, (name, opers)) =
470            OPS.iter()
471                .find(|(op, _)| *op == instr.opcode)
472                .ok_or_else(|| {
473                    anyhow::anyhow!(
474                        "Unknown opcode: 0x{:04X} at offset 0x{:08X}",
475                        instr.opcode,
476                        instr.offset
477                    )
478                })?;
479        if let Some(name) = name {
480            write!(writer, "0x{:08X} {}", instr.offset, name)?;
481        } else {
482            write!(writer, "0x{:08X} 0x{:04X}", instr.offset, instr.opcode)?;
483        }
484        for i in 0..instr.operands.len() {
485            writer.write_all(if i == 0 { b" " } else { b", " })?;
486            let value = instr.operands[i].value();
487            let mut typ = opers[i];
488            if typ == L {
489                if instr.operands[i].typ() == OperandType::Literal {
490                    write!(writer, "#0x{:08X}", self.label_offsets[value as usize - 1])?;
491                } else {
492                    typ = P;
493                }
494            }
495            if typ == P {
496                match instr.operands[i].typ() {
497                    OperandType::Literal => write!(writer, "0x{:08X}", value)?,
498                    OperandType::Variable => write!(writer, "var_{}", value)?,
499                    OperandType::Argument => write!(writer, "arg_{}", value)?,
500                    OperandType::UNK => {
501                        write!(writer, "{}:[0x{:08X}]", instr.operands[i].raw_type(), value)?
502                    }
503                }
504            } else if typ == I {
505                write!(writer, "0x{:08X}", value)?;
506            }
507        }
508        writeln!(writer)?;
509        if instr.opcode == RET {
510            writeln!(writer)?;
511        }
512        Ok(())
513    }
514
515    fn find_user_message_functions(&mut self) -> Result<()> {
516        let mut current_func_args = None;
517        self.reader.pos = CODE_OFFSET as usize;
518        let len = self.reader.data.len();
519        while self.reader.pos < len {
520            let instr = self.read_instruction()?;
521            if instr.is_message() {
522                if let Some((func_offset, func_num_args)) = current_func_args {
523                    if self.stack.len() >= 4 {
524                        let _number = self.stack.pop().unwrap();
525                        let name = self.stack.pop().unwrap();
526                        let message = self.stack.pop().unwrap();
527                        if name.typ() == OperandType::Argument
528                            && message.typ() == OperandType::Argument
529                        {
530                            self.user_message_functions.insert(
531                                func_offset,
532                                UserMessageFunction {
533                                    num_args: func_num_args,
534                                    name_arg_index: name.value() - 1,
535                                    message_arg_index: message.value() - 1,
536                                },
537                            );
538                            current_func_args = None;
539                        }
540                    }
541                }
542                self.stack.clear();
543                self.variables.clear();
544                continue;
545            }
546            match instr.opcode {
547                ENTER => {
548                    current_func_args = Some((instr.offset, instr.operands[0].value()));
549                    self.stack.clear();
550                    self.variables.clear();
551                }
552                MOV if instr.operands[0].typ() == OperandType::Variable => {
553                    self.variables
554                        .insert(instr.operands[0].value(), instr.operands[1]);
555                }
556                PUSH => {
557                    if instr.operands[0].typ() == OperandType::Variable
558                        && self.variables.contains_key(&instr.operands[0].value())
559                    {
560                        let var = self.variables.get(&instr.operands[0].value()).unwrap();
561                        self.stack.push(*var);
562                    } else {
563                        self.stack.push(instr.operands[0]);
564                    }
565                }
566                RET => {
567                    current_func_args = None;
568                    self.stack.clear();
569                    self.variables.clear();
570                }
571                _ => {
572                    self.stack.clear();
573                    self.variables.clear();
574                }
575            }
576        }
577        Ok(())
578    }
579
580    fn handle_mov_instruction(&mut self, instr: Instruction) -> Result<()> {
581        if instr.operands[0].typ() == OperandType::Variable {
582            self.variables
583                .insert(instr.operands[0].value(), instr.operands[1]);
584        }
585        Ok(())
586    }
587
588    fn handle_push_instruction(&mut self, instr: Instruction) -> Result<bool> {
589        let mut is_hover_text_push = false;
590        if instr.operands[0].typ() == OperandType::Variable
591            && self.variables.contains_key(&instr.operands[0].value())
592        {
593            let var = self.variables.get(&instr.operands[0].value()).unwrap();
594            if self.pre_is_hover_text_move {
595                if let Some(hover_text) = self.hover_text {
596                    if instr.operands[0].value() == hover_text {
597                        is_hover_text_push = true;
598                    } else {
599                        self.hover_text = None;
600                    }
601                }
602            }
603            self.stack.push(*var);
604        } else {
605            if self.pre_is_hover_text_binxor
606                && instr.operands[0].raw_type() == 5
607                && instr.operands[0].value() & 0x10000 != 0
608            {
609                if let Some(hover_text) = self.hover_text {
610                    if let Some(var) = self.variables.get(&hover_text) {
611                        if var.typ() == OperandType::Literal && var.value() < 0xFFFFFFF {
612                            self.strs.push(PalString {
613                                offset: var.offset,
614                                typ: StringType::Hover,
615                            });
616                        }
617                    }
618                    self.hover_text = None;
619                }
620            } else {
621                self.hover_text = None;
622            }
623            self.stack.push(instr.operands[0]);
624        }
625        Ok(is_hover_text_push)
626    }
627
628    fn handle_call_instruction(&mut self, instr: Instruction) -> Result<()> {
629        self.handle_call_instruction_internal(instr)?;
630        self.stack.clear();
631        self.variables.clear();
632        Ok(())
633    }
634
635    fn handle_call_instruction_internal(&mut self, instr: Instruction) -> Result<()> {
636        if self.label_offsets.is_empty() || instr.operands[0].typ() != OperandType::Literal {
637            return Ok(());
638        }
639        let target_offset = self.label_offsets[instr.operands[0].value() as usize - 1];
640        let message_func = match self.user_message_functions.get(&target_offset) {
641            Some(func) => func,
642            None => return Ok(()),
643        };
644        if self.stack.len() < message_func.num_args as usize {
645            return Ok(());
646        }
647        let mut args = Vec::new();
648        for _ in 0..message_func.num_args {
649            args.push(self.stack.pop().unwrap());
650        }
651        args.reverse();
652        let name = args[message_func.name_arg_index as usize];
653        let message = args[message_func.message_arg_index as usize];
654        if name.typ() == OperandType::Literal && message.typ() == OperandType::Literal {
655            self.strs.push(PalString {
656                offset: name.offset,
657                typ: StringType::Name,
658            });
659            self.strs.push(PalString {
660                offset: message.offset,
661                typ: StringType::Message,
662            });
663        }
664        Ok(())
665    }
666
667    fn handle_syscall_instruction(&mut self, instr: Instruction) -> Result<()> {
668        match instr.operands[0].raw_value {
669            0x60002 => {
670                self.handle_select_choice_instruction()?;
671            }
672            0x20014 => {
673                self.handle_another_message()?;
674            }
675            0xf0002 => {
676                self.handle_label()?;
677            }
678            _ => {
679                self.stack.clear();
680            }
681        }
682        Ok(())
683    }
684
685    fn handle_message_instruction(&mut self) -> Result<()> {
686        self.handle_message_instruction_internal()?;
687        self.stack.clear();
688        self.variables.clear();
689        Ok(())
690    }
691
692    fn handle_another_message(&mut self) -> Result<()> {
693        if self.stack.len() < 3 {
694            return Ok(());
695        }
696        let _message_id = self.stack.pop().unwrap();
697        let name = self.stack.pop().unwrap();
698        let message = self.stack.pop().unwrap();
699        if name.typ() != OperandType::Literal || message.typ() != OperandType::Literal {
700            return Ok(());
701        }
702        self.strs.push(PalString {
703            offset: name.offset,
704            typ: StringType::Name,
705        });
706        self.strs.push(PalString {
707            offset: message.offset,
708            typ: StringType::Message,
709        });
710        Ok(())
711    }
712
713    fn handle_label(&mut self) -> Result<()> {
714        if self.stack.len() < 1 {
715            return Ok(());
716        }
717        let label = self.stack.pop().unwrap();
718        if label.typ() != OperandType::Literal {
719            return Ok(());
720        }
721        self.strs.push(PalString {
722            offset: label.offset,
723            typ: StringType::Label,
724        });
725        Ok(())
726    }
727
728    fn handle_message_instruction_internal(&mut self) -> Result<()> {
729        if self.stack.len() < 4 {
730            return Ok(());
731        }
732        let _number = self.stack.pop().unwrap();
733        let name = self.stack.pop().unwrap();
734        let message = self.stack.pop().unwrap();
735        if name.typ() != OperandType::Literal || message.typ() != OperandType::Literal {
736            return Ok(());
737        }
738        self.strs.push(PalString {
739            offset: name.offset,
740            typ: StringType::Name,
741        });
742        self.strs.push(PalString {
743            offset: message.offset,
744            typ: StringType::Message,
745        });
746        Ok(())
747    }
748
749    fn handle_select_choice_instruction(&mut self) -> Result<()> {
750        self.handle_select_choice_instruction_internal()?;
751        self.stack.clear();
752        self.variables.clear();
753        Ok(())
754    }
755
756    fn handle_select_choice_instruction_internal(&mut self) -> Result<()> {
757        if self.stack.len() < 1 {
758            return Ok(());
759        }
760        let choice = self.stack.pop().unwrap();
761        self.strs.push(PalString {
762            offset: choice.offset,
763            typ: StringType::Message,
764        });
765        Ok(())
766    }
767}